package com.lanyou.esb.cook.proxy.redis.proxy.base;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisException;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.lanyou.esb.cook.proxy.redis.JedisPoolUtils;
import com.lanyou.esb.cook.proxy.redis.proxy.HashJedisProxy;

/**
 * Hash数据类型,操作Redis代理类基类
 * 
 * @author Davey.wu
 */
public abstract class BaseHashJedisProxy<T> extends BaseJedisProxy<T> implements
		HashJedisProxy<T> {
	/** 组成HASH键的域及其在键中的位置 **/
	private Map<String, Integer> fields;

	@Override
	public String getKeyFromObject(T target) {
		return this.generateKey(this.getKeyMap(target));
	}

	@Override
	public String generateKey(Map<String, String> map) {
		if (this.getFields() == null || this.getFields().isEmpty()) {
			return "";
		}
		String result = "";
		int size = this.getFields().size();
		String[] keys = new String[size];
		for (Entry<String, Integer> entry : this.getFields().entrySet()) {
			if (entry.getValue() >= 0 && entry.getValue() < size) {
				keys[entry.getValue()] = entry.getKey();
			}
		}
		for (int i = 0; i < size; ++i) {
			if (StringUtils.isBlank(keys[i])) {
				continue;
			}
			String value = map.get(keys[i]);
			if (StringUtils.isBlank(value)) {
				continue;
			}
			result += StringUtils.isBlank(result) ? value
					: BaseJedisProxy.KEY_SEPARATOR + value;
		}
		return result;
	}

	@Override
	public Map<String, Object> decodeKey(String key) {
		if (StringUtils.isBlank(key)) {
			return new HashMap<>(0);
		}
		if (this.getFields() == null || this.getFields().isEmpty()) {
			return new HashMap<>(0);
		}

		String[] values = key.split(BaseJedisProxy.KEY_SEPARATOR);
		Map<String, Object> result = new HashMap<>(values.length);
		for (Entry<String, Integer> entry : this.getFields().entrySet()) {
			int index = entry.getValue();
			if (index >= 0 && index < values.length) {
				result.put(entry.getKey(), values[index]);
			}
		}
		return result;
	}

	@Override
	public T hget(String key) throws JedisException, JsonParseException,
			JsonMappingException, IOException {
		Jedis jedis = JedisPoolUtils.getJedis();
		if (jedis == null) {
			return null;
		}
		T target = null;
		String targetStr = "";

		boolean broken = false;
		try {
			targetStr = jedis.hget(this.getKey(), key);
		} catch (Exception e) {
			broken = true;
		} finally {
			JedisPoolUtils.returnRes(jedis, broken);
		}
		if (StringUtils.isNotBlank(targetStr)) {
			target = fromJson(targetStr);
		}
		return target;
	}

	@Override
	public long hset(String key, T target) throws JedisException,
			JsonProcessingException {
		Jedis jedis = JedisPoolUtils.getJedis();
		if (jedis == null) {
			return 0;
		}
		Long result = 0l;
		boolean broken = false;
		try {
			result = jedis.hset(this.getKey(), key, this.toJson(target));
		} catch (Exception e) {
			broken = true;
		} finally {
			JedisPoolUtils.returnRes(jedis, broken);
		}
		if (result == null) {
			return 0l;
		}
		return result;
	}

	@Override
	public void loadHashAll(List<T> list) throws JedisException,
			JsonProcessingException {
		if (list == null || list.isEmpty()) {
			return;
		}
		Jedis jedis = JedisPoolUtils.getJedis();
		if (jedis == null) {
			return;
		}
		boolean broken = false;
		try {
			Pipeline p = jedis.pipelined();
			for (T t : list) {
				p.hset(this.getKey(), this.getKeyFromObject(t),
						this.toJson(t));
			}
			p.sync();
		} catch (Exception e) {
			broken = true;
		} finally {
			JedisPoolUtils.returnRes(jedis, broken);
		}
	}

	@Override
	public void reloadHash(T target) throws JedisException,
			JsonProcessingException {
		this.hset(this.getKeyFromObject(target), target);
	}

	public void setFields(Map<String, Integer> fields) {
		this.fields = fields;
	}

	@Override
	public Map<String, Integer> getFields() {
		return fields;
	}
}
